dojo.provide('hp.iPhoneButton');//This class Name
//This is a widget so it extends widget and templated
dojo.require('dijit._Widget');
dojo.require('dijit._Templated');
//These classes are required for the drag and drop feature along with
//the animation
dojo.require('dojo.dnd.move');
dojo.require('dojo.fx');

dojo.declare("hp.iPhoneButton", [dijit._Widget, dijit._Templated],
{
    // summary:
    //          A class that represents an iPhone style button
    // description:
    //          This class is creates the backend functionality for
    //          the iphone style toggle button.  Its movements are animated and the user
    //          click either drag the handle or click any wear in order to toggle it.
    //          The text, width, and animation speed can all be set in the constructor 
    //          example: new hp.iPhoneButton({id:"checkbox",onText:"ON",offText:"OFF",width:200,animateSpeed:200,startOn:true});

    realWidth:0,/*number-The actual width of the div*/
    container:null,/*the dnd container for the slider thing*/
    left:0,/*The last known position of the handle*/
    animateSpeed:200,/*The duration in ms of the animation*/
    startOn:false,/*If this is true we'll swap the starting position to true*/
    value:false,/*value of the checkbox*/
    curText:"",/*the currently selected text*/
    width:89,/*the width that the users sets*/
    onText:"ON", /*The text for the on state*/
    offText:"OFF", /* The text for the off state*/
    templateString:'<div dojoAttachPoint="outsideContainer" class="iPhoneCheckContainer"><label class="iPhoneCheckLabelOff" id="" dojoAttachPoint="offLabel" ><span dojoAttachPoint="offSpan"></span></label><label dojoAttachPoint="onLabel" class="iPhoneCheckLabelOn" ><span dojoAttachPoint="onSpan"></span></label><div dojoAttachPoint="handle" class="iPhoneCheckHandle" style="width: 33px;"><div class="iPhoneCheckHandleRight"><div class="iPhoneCheckHandleCenter"></div></div></div></div>',
    constructor:function(args){
        // summary:
        //          Don't really need to do anything here... just call the methods of the super classes
        this.inherited(arguments);
    },
    _attachListeners:function(){
        // summary:
        //          Attaches Listeners to the interactive parts of the button
        // description:
        //          this.container is a DnD container, so the onMove and onMoveStop events
        //          are specific to the dojo.dnd.move class that I'm using here.
        //          There is also a listener for just any clicks which will cause us to toggle
        dojo.connect(this.container, "onMove", this, this._dragging);
        dojo.connect(this.container, "onMoveStop", this, this._dragged);
        dojo.connect(this.outsideContainer, "onclick", this, this._switchPosition);
    },
    _createDnD:function(){
        // summary:
        //          creates the dnd object
        // description:
        //          the box args tell how far the handle is allowed to be dragged.  Its constrained to the 
        //          size of the button basically
        var boxargs = {
            box:
            {
                l:0,//left
                t:0,//top
                w:this.realWidth-3,//width
                h:27//height
            },
            within:true
        };
        this.container = new dojo.dnd.move.boxConstrainedMoveable(this.handle,boxargs);
    },
    _setInitialStyles:function(){
        // summary:
        //          Inject styles and copy into the template
        // description:
        //          the template starts of with no text or sizing, so this updates the styles on
        //          the various attach points for the template.
        this.onSpan.innerHTML = this.onText;
        this.offSpan.innerHTML = this.offText;
        var width = this.width || 89;
        if(typeof width === "string"){
            width = 89;
        }
        this.realWidth = width;
        dojo.style(this.outsideContainer, "width", width+"px");
        dojo.style(this.offLabel, "width", (width-5)+"px");
        dojo.style(this.onLabel, "width", "0px");
        dojo.style(this.onSpan, "marginLeft", "-50px");
        dojo.style(this.handle, "left", "0px");
        if(this.startOn){
            this.instantToggle();
        }
    },
    postCreate:function(){
        // summary:
        //          this is really the controller for the creation of the widget.  It just has all the function calls.
        this.inherited(arguments);
        this._setInitialStyles();
        this._createDnD();
        this._attachListeners();
    },
    _switchPosition:function(evt){
        // summary:
        //          called to switch the position 
        // description:
        //          This is an internal only method that gets called when onClick happens
        //          on the outer div.  onClick always gets fired, so if there was a drag we 
        //          don't want to do anything, but if it wasn't dragged then we want to toggle 
        //          the position of the handle.
        if(this.wasDragged){
            this.wasDragged = false;
        }else{
            this.toggle();
        }
    },
    _dragging:function(mover, leftTop){
        // summary:
        //          passes the appropriate values to the move function
        this.wasDragged = true;
        this._move(leftTop);
    },
    _dragged:function(mover){
        // summary:
        //          when the handle is done being dragged this gets called and the handle snaps right or left
        this._snap();
        this._updateValue();//This is a bit of bad design, but if I put this function call in snap it gets called twice
    },
    _move:function(leftTop){
        // summary:
        //          function to update the position of the handle
        // description:
        //          while the handle is being dragged, this function gets called to update the postion of the left
        //          and right sides of the toggle based on the position of the handle.  This is what make it seem like its
        //          one flowing element
        var left = leftTop.l;
        this.left = left;
        if(left > (this.realWidth - 30) || left < 0){
            return;
        }
        dojo.style(this.offSpan, "marginRight", -left+"px"); 
        dojo.style(this.offLabel, "width", this.realWidth - 30 - left+"px");
        dojo.style(this.onSpan, "marginLeft", left - (this.realWidth - 30) +"px"); 
        dojo.style(this.onLabel, "width", left+3+"px");
    },
    instantToggle:function(){
        // summary:
        //          this is a function that would probably only be used internally for toggling with no animation
        // description:
        //          i use this function on post create to start the button off in the right state.  it
        //          doesn't use any animation, so if a user wanted to switch with no animation they could use attr('value') and then toggle
        var tmpLeft = 0;
        var leftHandle = 0;
        if(this.left === 0){
            this.left = this.realWidth - 30;
            tmpLeft = this.left;
            leftHandle = this.left - 9;
            this._isCurrentlyLeft = false;
        }else{
            this.left = 0;
            tmpLeft = this.left;
            leftHandle = this.left;
            this._isCurrentlyLeft = true;
        }

        dojo.style(this.offSpan, "marginRight", -tmpLeft+"px"); //init 0
        dojo.style(this.offLabel, "width", this.realWidth - 30 - tmpLeft+"px"); //init -50
        dojo.style(this.onSpan, "marginLeft", tmpLeft - (this.realWidth - 30) +"px"); //init -50
        dojo.style(this.onLabel, "width", tmpLeft+3+"px"); //init -50
        dojo.style(this.handle, "left", leftHandle+"px"); //init -50
        this._updateValue();
    },
    toggle:function(){
        // summary:
        //          animated toggle function
        // description:
        //          user available call to toggle the button with animation and appropriate calls to events
        this._snap(true);
        this._updateValue();
    },
    _snap:function(swap){
        // summary:
        //          responsible for animating the move of the handle after the user drops it
        // description: 
        //          this function figures out where the handle currently is and based on the position
        //          if its closer to one side or another, it animate the transtion to that side.  
        //          there are several properties on several elements that need to be animated all together
        //          in order for this to happen.  Fortunately dojo.fx.combine makes this easy
        var curLeft = this.left;
        var goLeft = this.realWidth - 30;
        var leftHandle = this.realWidth - 39;
        var curLeftHandle = curLeft;
        if((this.realWidth-30)/2 > curLeft){
            goLeft = 0;
            leftHandle = 0;
            this._isCurrentlyLeft = true;
        }else{
            this._isCurrentlyLeft = false;
        }
        if(swap){
            if(goLeft === 0){
                goLeft = this.realWidth - 30;
                leftHandle = this.realWidth - 39;
                this._isCurrentlyLeft = false;
            }else{
                goLeft = 0;
                leftHandle = 0;
                curLeftHandle = this.realWidth -39;
                this._isCurrentlyLeft = true;
            }
            this.left = goLeft;
        }
        var animOne = dojo.animateProperty({
          node:this.offSpan,
          duration:this.animateSpeed,
          properties: {
              marginRight: {end:-goLeft,start:-curLeft}
          }
        });
        var animTwo = dojo.animateProperty({
          node:this.offLabel,
          duration:this.animateSpeed,
          properties: {
              width: {end:this.realWidth - 30 - goLeft,start:this.realWidth - 30 - curLeft}
          }
        });
        var animThree = dojo.animateProperty({
          node:this.onSpan,
          duration:this.animateSpeed,
          properties: {
              marginLeft: {end:goLeft - (this.realWidth - 30),start:curLeft - (this.realWidth - 30) }
          }
        });
        var animFour = dojo.animateProperty({
          node:this.onLabel,
          duration:this.animateSpeed,
          properties: {
              width: {end:goLeft+3,start:curLeft+3}
          }
        });
        var animFive = dojo.animateProperty({
          node:this.handle,
          duration:this.animateSpeed,
          properties: {
              left: {end:leftHandle,start:curLeftHandle}
          }
        });
        var combinedAnim = dojo.fx.combine([animOne,animTwo,animThree,animFour,animFive]);
        combinedAnim.play();
    },
    _updateValue:function(){
        // summary:
        //          keeps the value and curtext fields up to date
        if(this._isCurrentlyLeft){
            this.value = false;
            this.curText = this.offText;
        }else{
            this.value = true;
            this.curText = this.onText;
        }
        this.onChange();
    },
    onChange:function(){
        // summary:
        //          for whatever reason, widget,templated don't fire onChange for this 
        //          but they do fire onclick... so i just fire it myself
    }
});
